package com.fhzn.auth.tools;

import com.alibaba.fastjson.JSONObject;
import com.fhzn.auth.constant.Constants;
import com.fhzn.auth.entity.*;
import com.fhzn.auth.enums.RoleGroupEnum;
import com.fhzn.auth.mapper.*;
import com.fhzn.auth.web.request.UserAddRequest;
import com.fhzn.auth.web.request.UserModifyRequest;
import com.fhzn.commons.toolkit.exception.BuzException;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author fhzn
 * 用户管理类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class UserManager {

    private final RoleMapper roleMapper;
    private final UserRoleMapper userRoleMapper;
    private final RoleRelationMapper roleRelationMapper;
    private final UserMapper userMapper;
    private final UserRelationMapper userRelationMapper;


    /**
     * 判断用户是否存在
     *
     * @param nickName 用户昵称
     * @return true/false
     */
    public Boolean checkUser(String nickName) {
        User user = this.getUser(nickName);
        if (user == null) {
            throw new BuzException("对应用户不存在");
        }
        return true;
    }

    /**
     * 根据昵称查询用户
     *
     * @param nickName 用户昵称
     * @return 用户信息
     */
    public User getUser(String nickName) {
        return userMapper.findUserByNickName(nickName);
    }


    /**
     * 根据昵称查询用户
     *
     * @param nickNameList 用户昵称列表
     * @return 用户信息列表
     */
    public List<User> getUserByNickNames(List<String> nickNameList) {
        return userMapper.listUserByNickNames(nickNameList);
    }

    /**
     * 判断能否操作用户(添加/新增)
     * 管理员/超级管理员(wups管理员) 可以操作所有类型用户
     *
     * @param handler 用户昵称
     * @return true/false
     */
    public boolean canOperatorUser(String handler) {
        List<Role> roles = roleMapper.findRolesByNickname(handler);
        return !AuthUtil.isAdminOrSuperAdmin(roles);
    }

    /**
     * 获取操作者能操作的角色列表
     * 操作者已有角色和期望赋予角色的交集
     *
     * @param targetRoles   目标角色id
     * @param operatorRoles 操作角色id
     * @return 角色id列表
     */
    public List<Integer> canOperatorRoles(List<Integer> targetRoles, List<Integer> operatorRoles) {
        Set<Integer> targetRoleSet = Sets.newHashSet(targetRoles);
        Set<Integer> operatorRoleSet = Sets.newHashSet(operatorRoles);
        return Lists.newArrayList(Sets.intersection(targetRoleSet, operatorRoleSet));
    }

    /**
     * 获取真实添加的角色列表
     * 添加的角色列表和已有角色列表的差集
     *
     * @param targetRoles 目标角色id
     * @param existRoles  角色id
     * @return 真是角色id
     */
    public List<Integer> realAddRoles(List<Integer> targetRoles, List<Integer> existRoles) {
        Set<Integer> targetRoleSet = Sets.newHashSet(targetRoles);
        Set<Integer> existRoleSet = Sets.newHashSet(existRoles);
        return Lists.newArrayList(Sets.difference(targetRoleSet, existRoleSet));
    }

    /**
     * 获取真实移除的角色列表
     * 待操作角色列表和已有角色列表的并集
     *
     * @param targetRoles 目标角色id
     * @param existRoles  角色id
     * @return 角色id列表
     */
    public List<Integer> realRemoveRoles(List<Integer> targetRoles, List<Integer> existRoles) {
        Set<Integer> targetRoleSet = Sets.newHashSet(targetRoles);
        Set<Integer> existRoleSet = Sets.newHashSet(existRoles);
        return Lists.newArrayList(Sets.intersection(targetRoleSet, existRoleSet));
    }


    /**
     * 获取操作者能分配的角色
     * 1. 超级管理员能分配所有角色
     * 2. 应用管理员能分配应用下所有角色
     * 3. 应用子管理员能分配所有下级角色
     * 4. 直属上级能分配自己角色下的子角色
     *
     * @param operatorNickName 操作人花名
     * @param targetUserId     备操作人userId
     * @return 角色列表
     */
    public List<Role> getCanOperatorRoles(String operatorNickName, Integer targetUserId) {
        List<Role> operatorRolesInDb = roleMapper.findRolesByNickname(operatorNickName);
        final Set<Role> canOperatorRoles = Sets.newHashSet();
        operatorRolesInDb.forEach(role -> {
            if (AuthUtil.isSuperAdmin(role)) {
                canOperatorRoles.addAll(roleMapper.queryAll());
            }
            if (AuthUtil.isAdmin(role)) {
                canOperatorRoles.addAll(roleMapper.findAllRolesByAppCode(role.getAppCode()));
            }
            if (AuthUtil.isSubAdmin(role)) {
                List<RoleRelation> roleRelations = roleRelationMapper.selectBySuperId(role.getId());
                List<Integer> roleIds = roleRelations.stream().map(RoleRelation::getRoleId).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(roleIds)) {
                    canOperatorRoles.addAll(roleMapper.findCanAppointRoles(roleIds));
                }
            }
        });
        // 判断在应用下是否有上下级关系
        if (targetUserId != null && targetUserId > 0) {
            User u = userMapper.findUserByNickName(operatorNickName);
            List<UserRelation> relations = userRelationMapper.findRelations(u.getId(), targetUserId);
            List<String> relativeAppCodes = relations.stream().map(UserRelation::getAppCode).collect(Collectors.toList());
            List<Role> handlerRoles = roleMapper.findRolesByNickname(operatorNickName);
            // 根据操作人自己拥有的角色去找到可分配的下级角色(也有可能为空)
            List<Role> roleListInSameApp = handlerRoles.stream().filter(r -> relativeAppCodes.contains(r.getAppCode())).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(roleListInSameApp)) {
                List<Role> leaderCanOpRoles = roleMapper.findCanAppointRoles(roleListInSameApp.stream()
                        .map(Role::getId)
                        .collect(Collectors.toList()));
                canOperatorRoles.addAll(leaderCanOpRoles);
            }
        }
        return Lists.newArrayList(canOperatorRoles);
    }

    /**
     * 获取用户在数据库中的角色
     *
     * @param nickName 用户昵称
     * @return 角色列表
     */
    public List<Role> getRolesInDb(String nickName) {
        return roleMapper.findRolesByNickname(nickName);
    }

    /**
     * 获取用户在数据库中的角色id
     *
     * @param nickName 用户昵称
     * @return 角色id列表
     */
    public List<Integer> getRoleIdsInDb(String nickName) {
        List<Role> operatorRoles = roleMapper.findRolesByNickname(nickName);
        return operatorRoles.stream().map(Role::getId).collect(Collectors.toList());
    }

    /**
     * 批量给用户添加角色
     *
     * @param roleIds      角色id列表
     * @param targetUserId 目标用户id
     * @param handler      用户昵称
     */
    public void addRoleRelationBatch(List<Integer> roleIds, Integer targetUserId, String handler) {
        List<UserRole> userRoles = buildUserRoles(roleIds, targetUserId, handler);
        userRoleMapper.batchInsert(userRoles);
    }

    /**
     * 批量的移除用户角色
     *
     * @param roleIds      角色id列表
     * @param targetUserId 目标角色id
     * @param handler      用户昵称
     */
    public void removeRoleRelationBatch(List<Integer> roleIds, Integer targetUserId, String handler) {
        List<UserRole> userRoles = buildUserRoles(roleIds, targetUserId, handler);
        userRoleMapper.deleteBatchIds(userRoles);
    }

    /**
     * 构造用户角色对象
     *
     * @param roleIds      角色id列表
     * @param targetUserId 目标用户id
     * @param handler      用户昵称
     * @return 用户角色列表
     */
    public List<UserRole> buildUserRoles(List<Integer> roleIds, Integer targetUserId, String handler) {
        List<UserRole> userRoles = Lists.newArrayList();
        roleIds.forEach(t -> {
            UserRole userRole = new UserRole();
            userRole.setUserId(targetUserId);
            userRole.setRoleId(t);
            userRole.setCreator(handler);
            userRole.setModifier(handler);
            userRoles.add(userRole);
        });
        return userRoles;
    }

    /**
     * 给用户添加默认角色(WUPS_DEFAULT)
     *
     * @param userId  用户id
     * @param handler 用户昵称
     */
    public void addDefaultRole(Integer userId, String handler) {
        Role role = roleMapper.getRoleByCode("WUPS_DEFAULT", "WUPS");
        if (role == null) {
            log.error("权限系统没有配置默认角色");
            return;
        }
        this.addRoleRelationBatch(Lists.newArrayList(role.getId()), userId, handler);
    }


    /**
     * 增加漫财用户并赋予增加默认角色组
     *
     * @param appCode 应用code
     * @param userId  用户id
     */
    public void addUserDefaultRoleList(String appCode, Integer userId) {

        List<Role> roleList = roleMapper.queryRoleByGroup(appCode, RoleGroupEnum.DEFAULT_ROLE.getSign());
        if (CollectionUtils.isEmpty(roleList)) {
            log.error("该 appCode {} 尚未配置默认角色组", appCode);
            return;
        }
        List<Integer> roleIdList = roleList.stream().map(Role::getId).collect(Collectors.toList());
        addRoleRelationBatch(roleIdList, userId, Constants.MANCAI_SYS_OPERATOR);
    }

    /**
     * 增加漫财用户并赋予增加默认角色组
     *
     * @param userAddDTO 用户添加请求
     * @return 用户信息
     */
    public User addManCaiUserWithDefaultRole(UserAddRequest userAddDTO) {
        String nickname = userAddDTO.getNickname();
        // 加下面这段逻辑临时充当幂等
        User user = userMapper.findUserByNickName(nickname);
        if (null != user) {
            return user;
        }
        user = addManCaiUser(userAddDTO);
        addUserDefaultRoleList(userAddDTO.getAppCode(), user.getId());
        return user;
    }


    /**
     * 增加漫财用户
     *
     * @param userModifyReq 用户编辑请求
     */
    public void updateManCaiUser(UserModifyRequest userModifyReq) {
        String nickname = userModifyReq.getNickname();
        User user = userMapper.findUserByNickName(nickname);
        if (null == user) {
            throw new BuzException("用户" + nickname + "不存在");
        }
        user = buildUserUpdatePO(user.getId(), userModifyReq);
        int i = userMapper.updateById(user);
        if (i != 1) {
            log.error("更新用户失败 modifyReq : {}", userModifyReq);
        }
    }

    /**
     * 构造漫财用户对象
     *
     * @param id            用户id
     * @param userModifyReq 用户编辑情况
     * @return 用户信息
     */
    private User buildUserUpdatePO(Integer id, UserModifyRequest userModifyReq) {
        User user = new User();
        user.setId(id);
        user.setNicknameCn(userModifyReq.getNicknameCn());
        user.setAppCode(userModifyReq.getAppCode());
        user.setStatus(userModifyReq.getStatus());
        user.setUserType(userModifyReq.getUserType());
        return user;
    }


    /**
     * 增加漫财用户
     *
     * @param userAddDTO 用户新增请求
     * @return 用户信息
     */
    public User addManCaiUser(UserAddRequest userAddDTO) {
        String nickname = userAddDTO.getNickname();
        User user = userMapper.findUserByNickName(nickname);
        if (null != user) {
            throw new BuzException("用户" + nickname + "已经存在");
        }
        user = buildManCaiUserPO(userAddDTO);
        userMapper.insert(user);
        return user;
    }


    /**
     * 根据昵称查询用户
     *
     * @param nickname 用户昵称
     * @return 用户信息
     */
    public User queryUserByNickName(String nickname) {
        User user = userMapper.findUserByNickName(nickname);
        if (user == null) {
            throw new BuzException("用户" + nickname + "不存在");
        }
        return user;
    }


    /**
     * 构造漫财用户对象
     *
     * @param userAddDTO 用户请求
     * @return 用户信息
     */
    private User buildManCaiUserPO(UserAddRequest userAddDTO) {
        String salt = RandomUtil.getRandomLetterAndNumberAndSymbol(AESEncryptorUtils.SALT_SIZE);
        User user = new User();
        user.setNickname(userAddDTO.getNickname());
        user.setNicknameCn(userAddDTO.getNicknameCn());
        user.setRealname(userAddDTO.getRealName());
        user.setCreator(Constants.MANCAI_SYS_OPERATOR);
        user.setModifier(Constants.MANCAI_SYS_OPERATOR);

        user.setAppCode(userAddDTO.getAppCode());
        user.setStatus(1);
        // 不传则默认设置为漫财用户
        Integer userType = Objects.isNull(userAddDTO.getUserType()) ? 9 : userAddDTO.getUserType();
        user.setUserType(userType);
        JSONObject obj = new JSONObject();
        obj.put("loanStation", "99");
        user.setUserExt(obj.toJSONString());
        user.setSalt(salt);
        // 默认密码超期
        user.setPasswordExpired(Constants.PASSWORD_EXPIRED);
        user.setPassword(AESEncryptorUtils.encrypt(RandomUtil.getRandomLetterAndNumberAndSymbol(8), salt));
        return user;
    }
}
